package com.gk.panda.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.gk.panda.commons.rsa.RsaService;
import com.gk.panda.gateway.service.UserService;
import com.gk.panda.gateway.util.ResponseUtil;
import com.gk.panda.gateway.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {

    private static final String LOGIN_URL = "/user/login";
    private static final String LOGOUT_URL = "/user/logout";

    private static Map<String,Integer> loginMap = new HashMap<>();

    @Autowired
    private UserService userService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private RsaService rsaService;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String url = request.getPath().value();
        if (url.equals(LOGIN_URL)) {
            MultiValueMap<String, String> params = request.getQueryParams();
            String uuid = params.getFirst("uuid");
            String code = params.getFirst("code");
            String username;
            String password;
            if (uuid.equals("APPLogin")) {
                username = params.getFirst("username");
                password = params.getFirst("password");
            } else {
                username = new String(Base64.getDecoder().decode(params.getFirst("username")));
                password = new String(Base64.getDecoder().decode(params.getFirst("password")));
            }

            String LargeScreen = params.getFirst("largeScreen");
            ValueOperations ops = redisTemplate.opsForValue();
            //先判断用户是否处于密码错误的冻结状态
            Object object = ops.get(username + "-login_error_time");
            if (!ObjectUtils.isEmpty(object)) {
                return ResponseUtil.writeError(response, 101, "账号密码错误次数过多，已暂时冻结账号，请15分钟后再试");
            }
            String token = null;
            UserVo userVo = userService.getByUsername(username);
            if (userVo == null) {
                return ResponseUtil.writeError(response,100,"账号不存在，请重新输入");
            } else if (userVo.getStatus() == 1) {
                return ResponseUtil.writeError(response,100,"账号已禁用，请联系管理员");
            }
            if (!uuid.equals("APPLogin")) {
//                if (LargeScreen != null && LargeScreen.equals("yes")){
//                    userVo.setLoginWay("APP/LS");
//                } else {
                    userVo.setLoginWay("PC");
//                }
                String redisCode = (String) ops.get("KAPTCHA_KEY" + uuid);
                if (redisCode == null) return ResponseUtil.writeError(response,105,"验证码过期");
                if (!redisCode.equals(code)) return ResponseUtil.writeError(response,106,"验证码错误");
            } else {
                userVo.setLoginWay("APP/LS");
            }
            if (passwordEncoder.matches(password,userVo.getPassword())) {
                userVo.setPassword(null);
                token = rsaService.generateToken((JSONObject) JSONObject.toJSON(userVo));
                ops.setIfAbsent(userVo.getLoginWay() + "-access_token::"+userVo.getUsername(),token,30, TimeUnit.DAYS);
                token = (String) ops.get(userVo.getLoginWay() + "-access_token::"+userVo.getUsername());
                HttpHeaders responseHeaders = response.getHeaders();
                responseHeaders.add("access_token",token);
                responseHeaders.add("Access-Control-Expose-Headers","access_token");
                //登陆成功删除失败记录
                loginMap.remove(username);
                return ResponseUtil.writeResponse(response,userVo);
            } else {//密码错误
                Integer num = loginMap.get(username);
                if (num == null) {
                    num = 1;
                } else {
                    num++;
                }
                if (num >= 5) {
                    ops.setIfAbsent(username+"-login_error_time",username,15,TimeUnit.MINUTES);
                    loginMap.remove(username);
                    return ResponseUtil.writeError(response, 101, "密码错误5次，已暂时冻结账号，请15分钟后再试");
                }
                loginMap.put(username,num);
                return ResponseUtil.writeError(response, 101, "密码错误，已错误" + num + "次，连续5次错误将暂时冻结账号");
            }
        } else if (url.equals(LOGOUT_URL)) {
            HttpHeaders requestHeaders = request.getHeaders();
            String token = requestHeaders.getFirst("Authorization");
            if (token == null) {
                MultiValueMap<String, String> params = request.getQueryParams();
                String username = params.getFirst("username");
                redisTemplate.delete("PC" + "-access_token::"+username);
                return ResponseUtil.writeResponse(response,"已退出登录");
            }
            Map<String, Object> map = rsaService.parseToken(token);
            String username = (String) map.get("username");
            String loginWay = (String) map.get("loginWay");
            redisTemplate.delete(loginWay + "-access_token::"+username);
            return ResponseUtil.writeResponse(response,"已退出登录");
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -2;
    }
}
